/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2006 Tim Angus

This file is part of Tremulous.

Tremulous is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Tremulous is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Tremulous; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

#include "q_shared.h"
#include "qcommon.h"

//script flags
#define SCFL_NOERRORS             0x0001
#define SCFL_NOWARNINGS           0x0002
#define SCFL_NOSTRINGWHITESPACES  0x0004
#define SCFL_NOSTRINGESCAPECHARS  0x0008
#define SCFL_PRIMITIVE            0x0010
#define SCFL_NOBINARYNUMBERS      0x0020
#define SCFL_NONUMBERVALUES       0x0040

//token types
#define TT_STRING           1	// string
#define TT_LITERAL          2	// literal
#define TT_NUMBER           3	// number
#define TT_NAME             4	// name
#define TT_PUNCTUATION      5	// punctuation

//string sub type
//---------------
//    the length of the string
//literal sub type
//----------------
//    the ASCII code of the literal
//number sub type
//---------------
#define TT_DECIMAL          0x0008	// decimal number
#define TT_HEX              0x0100	// hexadecimal number
#define TT_OCTAL            0x0200	// octal number
#define TT_BINARY           0x0400	// binary number
#define TT_FLOAT            0x0800	// floating point number
#define TT_INTEGER          0x1000	// integer number
#define TT_LONG             0x2000	// long number
#define TT_UNSIGNED         0x4000	// unsigned number
//punctuation sub type
//--------------------
#define P_RSHIFT_ASSIGN       1
#define P_LSHIFT_ASSIGN       2
#define P_PARMS               3
#define P_PRECOMPMERGE        4

#define P_LOGIC_AND           5
#define P_LOGIC_OR            6
#define P_LOGIC_GEQ           7
#define P_LOGIC_LEQ           8
#define P_LOGIC_EQ            9
#define P_LOGIC_UNEQ          10

#define P_MUL_ASSIGN          11
#define P_DIV_ASSIGN          12
#define P_MOD_ASSIGN          13
#define P_ADD_ASSIGN          14
#define P_SUB_ASSIGN          15
#define P_INC                 16
#define P_DEC                 17

#define P_BIN_AND_ASSIGN      18
#define P_BIN_OR_ASSIGN       19
#define P_BIN_XOR_ASSIGN      20
#define P_RSHIFT              21
#define P_LSHIFT              22

#define P_POINTERREF          23
#define P_CPP1                24
#define P_CPP2                25
#define P_MUL                 26
#define P_DIV                 27
#define P_MOD                 28
#define P_ADD                 29
#define P_SUB                 30
#define P_ASSIGN              31

#define P_BIN_AND             32
#define P_BIN_OR              33
#define P_BIN_XOR             34
#define P_BIN_NOT             35

#define P_LOGIC_NOT           36
#define P_LOGIC_GREATER       37
#define P_LOGIC_LESS          38

#define P_REF                 39
#define P_COMMA               40
#define P_SEMICOLON           41
#define P_COLON               42
#define P_QUESTIONMARK        43

#define P_PARENTHESESOPEN     44
#define P_PARENTHESESCLOSE    45
#define P_BRACEOPEN           46
#define P_BRACECLOSE          47
#define P_SQBRACKETOPEN       48
#define P_SQBRACKETCLOSE      49
#define P_BACKSLASH           50

#define P_PRECOMP             51
#define P_DOLLAR              52

//name sub type
//-------------
//    the length of the name

//punctuation
typedef struct punctuation_s
{
	char           *p;			//punctuation character(s)
	int             n;			//punctuation indication
	struct punctuation_s *next;	//next punctuation
} punctuation_t;

//token
typedef struct token_s
{
	char            string[MAX_TOKEN_CHARS];	//available token
	int             type;		//last read token type
	int             subtype;	//last read token sub type
	unsigned long int intvalue;	//integer value
	double          floatvalue;	//floating point value
	char           *whitespace_p;	//start of white space before token
	char           *endwhitespace_p;	//start of white space before token
	int             line;		//line the token was on
	int             linescrossed;	//lines crossed in white space
	struct token_s *next;		//next token in chain
} token_t;

//script file
typedef struct script_s
{
	char            filename[1024];	//file name of the script
	char           *buffer;		//buffer containing the script
	char           *script_p;	//current pointer in the script
	char           *end_p;		//pointer to the end of the script
	char           *lastscript_p;	//script pointer before reading token
	char           *whitespace_p;	//begin of the white space
	char           *endwhitespace_p;
	int             length;		//length of the script in bytes
	int             line;		//current line in script
	int             lastline;	//line before reading token
	int             tokenavailable;	//set by UnreadLastToken
	int             flags;		//several script flags
	punctuation_t  *punctuations;	//the punctuations used in the script
	punctuation_t **punctuationtable;
	token_t         token;		//available token
	struct script_s *next;		//next script in a chain
} script_t;


#define DEFINE_FIXED      0x0001

#define BUILTIN_LINE      1
#define BUILTIN_FILE      2
#define BUILTIN_DATE      3
#define BUILTIN_TIME      4
#define BUILTIN_STDC      5

#define INDENT_IF         0x0001
#define INDENT_ELSE       0x0002
#define INDENT_ELIF       0x0004
#define INDENT_IFDEF      0x0008
#define INDENT_IFNDEF     0x0010

//macro definitions
typedef struct define_s
{
	char           *name;		//define name
	int             flags;		//define flags
	int             builtin;	// > 0 if builtin define
	int             numparms;	//number of define parameters
	token_t        *parms;		//define parameters
	token_t        *tokens;		//macro tokens (possibly containing parm tokens)
	struct define_s *next;		//next defined macro in a list
	struct define_s *hashnext;	//next define in the hash chain
} define_t;

//indents
//used for conditional compilation directives:
//#if, #else, #elif, #ifdef, #ifndef
typedef struct indent_s
{
	int             type;		//indent type
	int             skip;		//true if skipping current indent
	script_t       *script;		//script the indent was in
	struct indent_s *next;		//next indent on the indent stack
} indent_t;

//source file
typedef struct source_s
{
	char            filename[MAX_QPATH];	//file name of the script
	char            includepath[MAX_QPATH];	//path to include files
	punctuation_t  *punctuations;	//punctuations to use
	script_t       *scriptstack;	//stack with scripts of the source
	token_t        *tokens;		//tokens to read first
	define_t       *defines;	//list with macro definitions
	define_t      **definehash;	//hash chain with defines
	indent_t       *indentstack;	//stack with indents
	int             skip;		// > 0 if skipping conditional code
	token_t         token;		//last read token
} source_t;

#define MAX_DEFINEPARMS     128

//directive name with parse function
typedef struct directive_s
{
	char           *name;
	int             (*func) (source_t * source);
} directive_t;

#define DEFINEHASHSIZE    1024

static int      Parse_ReadToken(source_t * source, token_t * token);

int             numtokens;

//list with global defines added to every source loaded
define_t       *globaldefines;

//longer punctuations first
punctuation_t   default_punctuations[] = {
	//binary operators
	{">>=", P_RSHIFT_ASSIGN, NULL},
	{"<<=", P_LSHIFT_ASSIGN, NULL},
	//
	{"...", P_PARMS, NULL},
	//define merge operator
	{"##", P_PRECOMPMERGE, NULL},
	//logic operators
	{"&&", P_LOGIC_AND, NULL},
	{"||", P_LOGIC_OR, NULL},
	{">=", P_LOGIC_GEQ, NULL},
	{"<=", P_LOGIC_LEQ, NULL},
	{"==", P_LOGIC_EQ, NULL},
	{"!=", P_LOGIC_UNEQ, NULL},
	//arithmatic operators
	{"*=", P_MUL_ASSIGN, NULL},
	{"/=", P_DIV_ASSIGN, NULL},
	{"%=", P_MOD_ASSIGN, NULL},
	{"+=", P_ADD_ASSIGN, NULL},
	{"-=", P_SUB_ASSIGN, NULL},
	{"++", P_INC, NULL},
	{"--", P_DEC, NULL},
	//binary operators
	{"&=", P_BIN_AND_ASSIGN, NULL},
	{"|=", P_BIN_OR_ASSIGN, NULL},
	{"^=", P_BIN_XOR_ASSIGN, NULL},
	{">>", P_RSHIFT, NULL},
	{"<<", P_LSHIFT, NULL},
	//reference operators
	{"->", P_POINTERREF, NULL},
	//C++
	{"::", P_CPP1, NULL},
	{".*", P_CPP2, NULL},
	//arithmatic operators
	{"*", P_MUL, NULL},
	{"/", P_DIV, NULL},
	{"%", P_MOD, NULL},
	{"+", P_ADD, NULL},
	{"-", P_SUB, NULL},
	{"=", P_ASSIGN, NULL},
	//binary operators
	{"&", P_BIN_AND, NULL},
	{"|", P_BIN_OR, NULL},
	{"^", P_BIN_XOR, NULL},
	{"~", P_BIN_NOT, NULL},
	//logic operators
	{"!", P_LOGIC_NOT, NULL},
	{">", P_LOGIC_GREATER, NULL},
	{"<", P_LOGIC_LESS, NULL},
	//reference operator
	{".", P_REF, NULL},
	//seperators
	{",", P_COMMA, NULL},
	{";", P_SEMICOLON, NULL},
	//label indication
	{":", P_COLON, NULL},
	//if statement
	{"?", P_QUESTIONMARK, NULL},
	//embracements
	{"(", P_PARENTHESESOPEN, NULL},
	{")", P_PARENTHESESCLOSE, NULL},
	{"{", P_BRACEOPEN, NULL},
	{"}", P_BRACECLOSE, NULL},
	{"[", P_SQBRACKETOPEN, NULL},
	{"]", P_SQBRACKETCLOSE, NULL},
	//
	{"\\", P_BACKSLASH, NULL},
	//precompiler operator
	{"#", P_PRECOMP, NULL},
	{"$", P_DOLLAR, NULL},
	{NULL, 0}
};

char            basefolder[MAX_QPATH];

/*
===============
Parse_CreatePunctuationTable
===============
*/
static void Parse_CreatePunctuationTable(script_t * script, punctuation_t * punctuations)
{
	int             i;
	punctuation_t  *p, *lastp, *newp;

	//get memory for the table
	if(!script->punctuationtable)
		script->punctuationtable = (punctuation_t **) Z_Malloc(256 * sizeof(punctuation_t *));
	Com_Memset(script->punctuationtable, 0, 256 * sizeof(punctuation_t *));
	//add the punctuations in the list to the punctuation table
	for(i = 0; punctuations[i].p; i++)
	{
		newp = &punctuations[i];
		lastp = NULL;
		//sort the punctuations in this table entry on length (longer punctuations first)
		for(p = script->punctuationtable[(unsigned int)newp->p[0]]; p; p = p->next)
		{
			if(strlen(p->p) < strlen(newp->p))
			{
				newp->next = p;
				if(lastp)
					lastp->next = newp;
				else
					script->punctuationtable[(unsigned int)newp->p[0]] = newp;
				break;
			}
			lastp = p;
		}
		if(!p)
		{
			newp->next = NULL;
			if(lastp)
				lastp->next = newp;
			else
				script->punctuationtable[(unsigned int)newp->p[0]] = newp;
		}
	}
}

/*
===============
Parse_ScriptError
===============
*/
static void QDECL Parse_ScriptError(script_t * script, char *str, ...)
{
	char            text[1024];
	va_list         ap;

	if(script->flags & SCFL_NOERRORS)
		return;

	va_start(ap, str);
	vsprintf(text, str, ap);
	va_end(ap);
	Com_Printf("file %s, line %d: %s\n", script->filename, script->line, text);
}

/*
===============
Parse_ScriptWarning
===============
*/
static void QDECL Parse_ScriptWarning(script_t * script, char *str, ...)
{
	char            text[1024];
	va_list         ap;

	if(script->flags & SCFL_NOWARNINGS)
		return;

	va_start(ap, str);
	vsprintf(text, str, ap);
	va_end(ap);
	Com_Printf("file %s, line %d: %s\n", script->filename, script->line, text);
}

/*
===============
Parse_SetScriptPunctuations
===============
*/
static void Parse_SetScriptPunctuations(script_t * script, punctuation_t * p)
{
	if(p)
		Parse_CreatePunctuationTable(script, p);
	else
		Parse_CreatePunctuationTable(script, default_punctuations);
	if(p)
		script->punctuations = p;
	else
		script->punctuations = default_punctuations;
}

/*
===============
Parse_ReadWhiteSpace
===============
*/
static int Parse_ReadWhiteSpace(script_t * script)
{
	while(1)
	{
		//skip white space
		while(*script->script_p <= ' ')
		{
			if(!*script->script_p)
				return 0;
			if(*script->script_p == '\n')
				script->line++;
			script->script_p++;
		}
		//skip comments
		if(*script->script_p == '/')
		{
			//comments //
			if(*(script->script_p + 1) == '/')
			{
				script->script_p++;
				do
				{
					script->script_p++;
					if(!*script->script_p)
						return 0;
				}
				while(*script->script_p != '\n');
				script->line++;
				script->script_p++;
				if(!*script->script_p)
					return 0;
				continue;
			}
			//comments /* */
			else if(*(script->script_p + 1) == '*')
			{
				script->script_p++;
				do
				{
					script->script_p++;
					if(!*script->script_p)
						return 0;
					if(*script->script_p == '\n')
						script->line++;
				}
				while(!(*script->script_p == '*' && *(script->script_p + 1) == '/'));
				script->script_p++;
				if(!*script->script_p)
					return 0;
				script->script_p++;
				if(!*script->script_p)
					return 0;
				continue;
			}
		}
		break;
	}
	return 1;
}

/*
===============
Parse_ReadEscapeCharacter
===============
*/
static int Parse_ReadEscapeCharacter(script_t * script, char *ch)
{
	int             c, val, i;

	//step over the leading '\\'
	script->script_p++;
	//determine the escape character
	switch (*script->script_p)
	{
		case '\\':
			c = '\\';
			break;
		case 'n':
			c = '\n';
			break;
		case 'r':
			c = '\r';
			break;
		case 't':
			c = '\t';
			break;
		case 'v':
			c = '\v';
			break;
		case 'b':
			c = '\b';
			break;
		case 'f':
			c = '\f';
			break;
		case 'a':
			c = '\a';
			break;
		case '\'':
			c = '\'';
			break;
		case '\"':
			c = '\"';
			break;
		case '\?':
			c = '\?';
			break;
		case 'x':
		{
			script->script_p++;
			for(i = 0, val = 0;; i++, script->script_p++)
			{
				c = *script->script_p;
				if(c >= '0' && c <= '9')
					c = c - '0';
				else if(c >= 'A' && c <= 'Z')
					c = c - 'A' + 10;
				else if(c >= 'a' && c <= 'z')
					c = c - 'a' + 10;
				else
					break;
				val = (val << 4) + c;
			}
			script->script_p--;
			if(val > 0xFF)
			{
				Parse_ScriptWarning(script, "too large value in escape character");
				val = 0xFF;
			}
			c = val;
			break;
		}
		default:				//NOTE: decimal ASCII code, NOT octal
		{
			if(*script->script_p < '0' || *script->script_p > '9')
				Parse_ScriptError(script, "unknown escape char");
			for(i = 0, val = 0;; i++, script->script_p++)
			{
				c = *script->script_p;
				if(c >= '0' && c <= '9')
					c = c - '0';
				else
					break;
				val = val * 10 + c;
			}
			script->script_p--;
			if(val > 0xFF)
			{
				Parse_ScriptWarning(script, "too large value in escape character");
				val = 0xFF;
			}
			c = val;
			break;
		}
	}
	//step over the escape character or the last digit of the number
	script->script_p++;
	//store the escape character
	*ch = c;
	//succesfully read escape character
	return 1;
}

/*
===============
Parse_ReadString

Reads C-like string. Escape characters are interpretted.
Quotes are included with the string.
Reads two strings with a white space between them as one string.
===============
*/
static int Parse_ReadString(script_t * script, token_t * token, int quote)
{
	int             len, tmpline;
	char           *tmpscript_p;

	if(quote == '\"')
		token->type = TT_STRING;
	else
		token->type = TT_LITERAL;

	len = 0;
	//leading quote
	token->string[len++] = *script->script_p++;
	//
	while(1)
	{
		//minus 2 because trailing double quote and zero have to be appended
		if(len >= MAX_TOKEN_CHARS - 2)
		{
			Parse_ScriptError(script, "string longer than MAX_TOKEN_CHARS = %d", MAX_TOKEN_CHARS);
			return 0;
		}
		//if there is an escape character and
		//if escape characters inside a string are allowed
		if(*script->script_p == '\\' && !(script->flags & SCFL_NOSTRINGESCAPECHARS))
		{
			if(!Parse_ReadEscapeCharacter(script, &token->string[len]))
			{
				token->string[len] = 0;
				return 0;
			}
			len++;
		}
		//if a trailing quote
		else if(*script->script_p == quote)
		{
			//step over the double quote
			script->script_p++;
			//if white spaces in a string are not allowed
			if(script->flags & SCFL_NOSTRINGWHITESPACES)
				break;
			//
			tmpscript_p = script->script_p;
			tmpline = script->line;
			//read unusefull stuff between possible two following strings
			if(!Parse_ReadWhiteSpace(script))
			{
				script->script_p = tmpscript_p;
				script->line = tmpline;
				break;
			}
			//if there's no leading double qoute
			if(*script->script_p != quote)
			{
				script->script_p = tmpscript_p;
				script->line = tmpline;
				break;
			}
			//step over the new leading double quote
			script->script_p++;
		}
		else
		{
			if(*script->script_p == '\0')
			{
				token->string[len] = 0;
				Parse_ScriptError(script, "missing trailing quote");
				return 0;
			}
			if(*script->script_p == '\n')
			{
				token->string[len] = 0;
				Parse_ScriptError(script, "newline inside string %s", token->string);
				return 0;
			}
			token->string[len++] = *script->script_p++;
		}
	}
	//trailing quote
	token->string[len++] = quote;
	//end string with a zero
	token->string[len] = '\0';
	//the sub type is the length of the string
	token->subtype = len;
	return 1;
}

/*
===============
Parse_ReadName
===============
*/
static int Parse_ReadName(script_t * script, token_t * token)
{
	int             len = 0;
	char            c;

	token->type = TT_NAME;
	do
	{
		token->string[len++] = *script->script_p++;
		if(len >= MAX_TOKEN_CHARS)
		{
			Parse_ScriptError(script, "name longer than MAX_TOKEN_CHARS = %d", MAX_TOKEN_CHARS);
			return 0;
		}
		c = *script->script_p;
	} while((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_');
	token->string[len] = '\0';
	//the sub type is the length of the name
	token->subtype = len;
	return 1;
}

/*
===============
Parse_NumberValue
===============
*/
static void Parse_NumberValue(char *string, int subtype, unsigned long int *intvalue, double *floatvalue)
{
	unsigned long int dotfound = 0;

	*intvalue = 0;
	*floatvalue = 0;
	//floating point number
	if(subtype & TT_FLOAT)
	{
		while(*string)
		{
			if(*string == '.')
			{
				if(dotfound)
					return;
				dotfound = 10;
				string++;
			}
			if(dotfound)
			{
				*floatvalue = *floatvalue + (double)(*string - '0') / (double)dotfound;
				dotfound *= 10;
			}
			else
			{
				*floatvalue = *floatvalue * 10.0 + (double)(*string - '0');
			}
			string++;
		}
		*intvalue = (unsigned long)*floatvalue;
	}
	else if(subtype & TT_DECIMAL)
	{
		while(*string)
			*intvalue = *intvalue * 10 + (*string++ - '0');
		*floatvalue = *intvalue;
	}
	else if(subtype & TT_HEX)
	{
		//step over the leading 0x or 0X
		string += 2;
		while(*string)
		{
			*intvalue <<= 4;
			if(*string >= 'a' && *string <= 'f')
				*intvalue += *string - 'a' + 10;
			else if(*string >= 'A' && *string <= 'F')
				*intvalue += *string - 'A' + 10;
			else
				*intvalue += *string - '0';
			string++;
		}
		*floatvalue = *intvalue;
	}
	else if(subtype & TT_OCTAL)
	{
		//step over the first zero
		string += 1;
		while(*string)
			*intvalue = (*intvalue << 3) + (*string++ - '0');
		*floatvalue = *intvalue;
	}
	else if(subtype & TT_BINARY)
	{
		//step over the leading 0b or 0B
		string += 2;
		while(*string)
			*intvalue = (*intvalue << 1) + (*string++ - '0');
		*floatvalue = *intvalue;
	}
}

/*
===============
Parse_ReadNumber
===============
*/
static int Parse_ReadNumber(script_t * script, token_t * token)
{
	int             len = 0, i;
	int             octal, dot;
	char            c;

//  unsigned long int intvalue = 0;
//  double floatvalue = 0;

	token->type = TT_NUMBER;
	//check for a hexadecimal number
	if(*script->script_p == '0' && (*(script->script_p + 1) == 'x' || *(script->script_p + 1) == 'X'))
	{
		token->string[len++] = *script->script_p++;
		token->string[len++] = *script->script_p++;
		c = *script->script_p;
		//hexadecimal
		while((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'A'))
		{
			token->string[len++] = *script->script_p++;
			if(len >= MAX_TOKEN_CHARS)
			{
				Parse_ScriptError(script, "hexadecimal number longer than MAX_TOKEN_CHARS = %d", MAX_TOKEN_CHARS);
				return 0;
			}
			c = *script->script_p;
		}
		token->subtype |= TT_HEX;
	}
#ifdef BINARYNUMBERS
	//check for a binary number
	else if(*script->script_p == '0' && (*(script->script_p + 1) == 'b' || *(script->script_p + 1) == 'B'))
	{
		token->string[len++] = *script->script_p++;
		token->string[len++] = *script->script_p++;
		c = *script->script_p;
		//binary
		while(c == '0' || c == '1')
		{
			token->string[len++] = *script->script_p++;
			if(len >= MAX_TOKEN_CHARS)
			{
				Parse_ScriptError(script, "binary number longer than MAX_TOKEN_CHARS = %d", MAX_TOKEN_CHARS);
				return 0;
			}
			c = *script->script_p;
		}
		token->subtype |= TT_BINARY;
	}
#endif							//BINARYNUMBERS
	else						//decimal or octal integer or floating point number
	{
		octal = qfalse;
		dot = qfalse;
		if(*script->script_p == '0')
			octal = qtrue;
		while(1)
		{
			c = *script->script_p;
			if(c == '.')
				dot = qtrue;
			else if(c == '8' || c == '9')
				octal = qfalse;
			else if(c < '0' || c > '9')
				break;
			token->string[len++] = *script->script_p++;
			if(len >= MAX_TOKEN_CHARS - 1)
			{
				Parse_ScriptError(script, "number longer than MAX_TOKEN_CHARS = %d", MAX_TOKEN_CHARS);
				return 0;
			}
		}
		if(octal)
			token->subtype |= TT_OCTAL;
		else
			token->subtype |= TT_DECIMAL;
		if(dot)
			token->subtype |= TT_FLOAT;
	}
	for(i = 0; i < 2; i++)
	{
		c = *script->script_p;
		//check for a LONG number
		if((c == 'l' || c == 'L') && !(token->subtype & TT_LONG))
		{
			script->script_p++;
			token->subtype |= TT_LONG;
		}
		//check for an UNSIGNED number
		else if((c == 'u' || c == 'U') && !(token->subtype & (TT_UNSIGNED | TT_FLOAT)))
		{
			script->script_p++;
			token->subtype |= TT_UNSIGNED;
		}
	}
	token->string[len] = '\0';
	Parse_NumberValue(token->string, token->subtype, &token->intvalue, &token->floatvalue);
	if(!(token->subtype & TT_FLOAT))
		token->subtype |= TT_INTEGER;
	return 1;
}

/*
===============
Parse_ReadPunctuation
===============
*/
static int Parse_ReadPunctuation(script_t * script, token_t * token)
{
	int             len;
	char           *p;
	punctuation_t  *punc;

	for(punc = script->punctuationtable[(unsigned int)*script->script_p]; punc; punc = punc->next)
	{
		p = punc->p;
		len = strlen(p);
		//if the script contains at least as much characters as the punctuation
		if(script->script_p + len <= script->end_p)
		{
			//if the script contains the punctuation
			if(!strncmp(script->script_p, p, len))
			{
				strncpy(token->string, p, MAX_TOKEN_CHARS);
				script->script_p += len;
				token->type = TT_PUNCTUATION;
				//sub type is the number of the punctuation
				token->subtype = punc->n;
				return 1;
			}
		}
	}
	return 0;
}

/*
===============
Parse_ReadPrimitive
===============
*/
static int Parse_ReadPrimitive(script_t * script, token_t * token)
{
	int             len;

	len = 0;
	while(*script->script_p > ' ' && *script->script_p != ';')
	{
		if(len >= MAX_TOKEN_CHARS)
		{
			Parse_ScriptError(script, "primitive token longer than MAX_TOKEN_CHARS = %d", MAX_TOKEN_CHARS);
			return 0;
		}
		token->string[len++] = *script->script_p++;
	}
	token->string[len] = 0;
	//copy the token into the script structure
	Com_Memcpy(&script->token, token, sizeof(token_t));
	//primitive reading successfull
	return 1;
}

/*
===============
Parse_ReadScriptToken
===============
*/
static int Parse_ReadScriptToken(script_t * script, token_t * token)
{
	//if there is a token available (from UnreadToken)
	if(script->tokenavailable)
	{
		script->tokenavailable = 0;
		Com_Memcpy(token, &script->token, sizeof(token_t));
		return 1;
	}
	//save script pointer
	script->lastscript_p = script->script_p;
	//save line counter
	script->lastline = script->line;
	//clear the token stuff
	Com_Memset(token, 0, sizeof(token_t));
	//start of the white space
	script->whitespace_p = script->script_p;
	token->whitespace_p = script->script_p;
	//read unusefull stuff
	if(!Parse_ReadWhiteSpace(script))
		return 0;

	script->endwhitespace_p = script->script_p;
	token->endwhitespace_p = script->script_p;
	//line the token is on
	token->line = script->line;
	//number of lines crossed before token
	token->linescrossed = script->line - script->lastline;
	//if there is a leading double quote
	if(*script->script_p == '\"')
	{
		if(!Parse_ReadString(script, token, '\"'))
			return 0;
	}
	//if an literal
	else if(*script->script_p == '\'')
	{
		//if (!Parse_ReadLiteral(script, token)) return 0;
		if(!Parse_ReadString(script, token, '\''))
			return 0;
	}
	//if there is a number
	else if((*script->script_p >= '0' && *script->script_p <= '9') ||
			(*script->script_p == '.' && (*(script->script_p + 1) >= '0' && *(script->script_p + 1) <= '9')))
	{
		if(!Parse_ReadNumber(script, token))
			return 0;
	}
	//if this is a primitive script
	else if(script->flags & SCFL_PRIMITIVE)
	{
		return Parse_ReadPrimitive(script, token);
	}
	//if there is a name
	else if((*script->script_p >= 'a' && *script->script_p <= 'z') ||
			(*script->script_p >= 'A' && *script->script_p <= 'Z') || *script->script_p == '_')
	{
		if(!Parse_ReadName(script, token))
			return 0;
	}
	//check for punctuations
	else if(!Parse_ReadPunctuation(script, token))
	{
		Parse_ScriptError(script, "can't read token");
		return 0;
	}
	//copy the token into the script structure
	Com_Memcpy(&script->token, token, sizeof(token_t));
	//succesfully read a token
	return 1;
}

/*
===============
Parse_StripDoubleQuotes
===============
*/
static void Parse_StripDoubleQuotes(char *string)
{
	if(*string == '\"')
	{
                memmove(string, string + 1, strlen(string));
	}
	if(string[strlen(string) - 1] == '\"')
	{
		string[strlen(string) - 1] = '\0';
	}
}

/*
===============
Parse_EndOfScript
===============
*/
static int Parse_EndOfScript(script_t * script)
{
	return script->script_p >= script->end_p;
}

/*
===============
Parse_LoadScriptFile
===============
*/
static script_t *Parse_LoadScriptFile(const char *filename)
{
	fileHandle_t    fp;
	char            pathname[MAX_QPATH];
	int             length;
	void           *buffer;
	script_t       *script;

	if(strlen(basefolder))
		Com_sprintf(pathname, sizeof(pathname), "%s/%s", basefolder, filename);
	else
		Com_sprintf(pathname, sizeof(pathname), "%s", filename);
	length = FS_FOpenFileRead(pathname, &fp, qfalse);
	if(!fp)
		return NULL;

	buffer = Z_Malloc(sizeof(script_t) + length + 1);
	Com_Memset(buffer, 0, sizeof(script_t) + length + 1);

	script = (script_t *) buffer;
	Com_Memset(script, 0, sizeof(script_t));
	strcpy(script->filename, filename);
	script->buffer = (char *)buffer + sizeof(script_t);
	script->buffer[length] = 0;
	script->length = length;
	//pointer in script buffer
	script->script_p = script->buffer;
	//pointer in script buffer before reading token
	script->lastscript_p = script->buffer;
	//pointer to end of script buffer
	script->end_p = &script->buffer[length];
	//set if there's a token available in script->token
	script->tokenavailable = 0;
	//
	script->line = 1;
	script->lastline = 1;
	//
	Parse_SetScriptPunctuations(script, NULL);
	//
	FS_Read(script->buffer, length, fp);
	FS_FCloseFile(fp);
	//
	script->length = Com_Compress(script->buffer);

	return script;
}

/*
===============
Parse_LoadScriptMemory
===============
*/
static script_t *Parse_LoadScriptMemory(char *ptr, int length, char *name)
{
	void           *buffer;
	script_t       *script;

	buffer = Z_Malloc(sizeof(script_t) + length + 1);
	Com_Memset(buffer, 0, sizeof(script_t) + length + 1);

	script = (script_t *) buffer;
	Com_Memset(script, 0, sizeof(script_t));
	strcpy(script->filename, name);
	script->buffer = (char *)buffer + sizeof(script_t);
	script->buffer[length] = 0;
	script->length = length;
	//pointer in script buffer
	script->script_p = script->buffer;
	//pointer in script buffer before reading token
	script->lastscript_p = script->buffer;
	//pointer to end of script buffer
	script->end_p = &script->buffer[length];
	//set if there's a token available in script->token
	script->tokenavailable = 0;
	//
	script->line = 1;
	script->lastline = 1;
	//
	Parse_SetScriptPunctuations(script, NULL);
	//
	Com_Memcpy(script->buffer, ptr, length);
	//
	return script;
}

/*
===============
Parse_FreeScript
===============
*/
static void Parse_FreeScript(script_t * script)
{
	if(script->punctuationtable)
		Z_Free(script->punctuationtable);
	Z_Free(script);
}

/*
===============
Parse_SetBaseFolder
===============
*/
static void Parse_SetBaseFolder(char *path)
{
	Com_sprintf(basefolder, sizeof(basefolder), path);
}

/*
===============
Parse_SourceError
===============
*/
static void QDECL Parse_SourceError(source_t * source, char *str, ...)
{
	char            text[1024];
	va_list         ap;

	va_start(ap, str);
	vsprintf(text, str, ap);
	va_end(ap);
	Com_Printf("file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text);
}

/*
===============
Parse_SourceWarning
===============
*/
static void QDECL Parse_SourceWarning(source_t * source, char *str, ...)
{
	char            text[1024];
	va_list         ap;

	va_start(ap, str);
	vsprintf(text, str, ap);
	va_end(ap);
	Com_Printf("file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text);
}

/*
===============
Parse_PushIndent
===============
*/
static void Parse_PushIndent(source_t * source, int type, int skip)
{
	indent_t       *indent;

	indent = (indent_t *) Z_Malloc(sizeof(indent_t));
	indent->type = type;
	indent->script = source->scriptstack;
	indent->skip = (skip != 0);
	source->skip += indent->skip;
	indent->next = source->indentstack;
	source->indentstack = indent;
}

/*
===============
Parse_PopIndent
===============
*/
static void Parse_PopIndent(source_t * source, int *type, int *skip)
{
	indent_t       *indent;

	*type = 0;
	*skip = 0;

	indent = source->indentstack;
	if(!indent)
		return;

	//must be an indent from the current script
	if(source->indentstack->script != source->scriptstack)
		return;

	*type = indent->type;
	*skip = indent->skip;
	source->indentstack = source->indentstack->next;
	source->skip -= indent->skip;
	Z_Free(indent);
}

/*
===============
Parse_PushScript
===============
*/
static void Parse_PushScript(source_t * source, script_t * script)
{
	script_t       *s;

	for(s = source->scriptstack; s; s = s->next)
	{
		if(!Q_stricmp(s->filename, script->filename))
		{
			Parse_SourceError(source, "%s recursively included", script->filename);
			return;
		}
	}
	//push the script on the script stack
	script->next = source->scriptstack;
	source->scriptstack = script;
}

/*
===============
Parse_CopyToken
===============
*/
static token_t *Parse_CopyToken(token_t * token)
{
	token_t        *t;

//  t = (token_t *) malloc(sizeof(token_t));
	t = (token_t *) Z_Malloc(sizeof(token_t));
//  t = freetokens;
	if(!t)
	{
		Com_Error(ERR_FATAL, "out of token space\n");
		return NULL;
	}
//  freetokens = freetokens->next;
	Com_Memcpy(t, token, sizeof(token_t));
	t->next = NULL;
	numtokens++;
	return t;
}

/*
===============
Parse_FreeToken
===============
*/
static void Parse_FreeToken(token_t * token)
{
	//free(token);
	Z_Free(token);
//  token->next = freetokens;
//  freetokens = token;
	numtokens--;
}

/*
===============
Parse_ReadSourceToken
===============
*/
static int Parse_ReadSourceToken(source_t * source, token_t * token)
{
	token_t        *t;
	script_t       *script;
	int             type, skip;

	//if there's no token already available
	while(!source->tokens)
	{
		//if there's a token to read from the script
		if(Parse_ReadScriptToken(source->scriptstack, token))
			return qtrue;
		//if at the end of the script
		if(Parse_EndOfScript(source->scriptstack))
		{
			//remove all indents of the script
			while(source->indentstack && source->indentstack->script == source->scriptstack)
			{
				Parse_SourceWarning(source, "missing #endif");
				Parse_PopIndent(source, &type, &skip);
			}
		}
		//if this was the initial script
		if(!source->scriptstack->next)
			return qfalse;
		//remove the script and return to the last one
		script = source->scriptstack;
		source->scriptstack = source->scriptstack->next;
		Parse_FreeScript(script);
	}
	//copy the already available token
	Com_Memcpy(token, source->tokens, sizeof(token_t));
	//free the read token
	t = source->tokens;
	source->tokens = source->tokens->next;
	Parse_FreeToken(t);
	return qtrue;
}

/*
===============
Parse_UnreadSourceToken
===============
*/
static int Parse_UnreadSourceToken(source_t * source, token_t * token)
{
	token_t        *t;

	t = Parse_CopyToken(token);
	t->next = source->tokens;
	source->tokens = t;
	return qtrue;
}

/*
===============
Parse_ReadDefineParms
===============
*/
static int Parse_ReadDefineParms(source_t * source, define_t * define, token_t ** parms, int maxparms)
{
	token_t         token, *t, *last;
	int             i, done, lastcomma, numparms, indent;

	if(!Parse_ReadSourceToken(source, &token))
	{
		Parse_SourceError(source, "define %s missing parms", define->name);
		return qfalse;
	}
	//
	if(define->numparms > maxparms)
	{
		Parse_SourceError(source, "define with more than %d parameters", maxparms);
		return qfalse;
	}
	//
	for(i = 0; i < define->numparms; i++)
		parms[i] = NULL;
	//if no leading "("
	if(strcmp(token.string, "("))
	{
		Parse_UnreadSourceToken(source, &token);
		Parse_SourceError(source, "define %s missing parms", define->name);
		return qfalse;
	}
	//read the define parameters
	for(done = 0, numparms = 0, indent = 0; !done;)
	{
		if(numparms >= maxparms)
		{
			Parse_SourceError(source, "define %s with too many parms", define->name);
			return qfalse;
		}
		if(numparms >= define->numparms)
		{
			Parse_SourceWarning(source, "define %s has too many parms", define->name);
			return qfalse;
		}
		parms[numparms] = NULL;
		lastcomma = 1;
		last = NULL;
		while(!done)
		{
			//
			if(!Parse_ReadSourceToken(source, &token))
			{
				Parse_SourceError(source, "define %s incomplete", define->name);
				return qfalse;
			}
			//
			if(!strcmp(token.string, ","))
			{
				if(indent <= 0)
				{
					if(lastcomma)
						Parse_SourceWarning(source, "too many comma's");
					lastcomma = 1;
					break;
				}
			}
			lastcomma = 0;
			//
			if(!strcmp(token.string, "("))
			{
				indent++;
				continue;
			}
			else if(!strcmp(token.string, ")"))
			{
				if(--indent <= 0)
				{
					if(!parms[define->numparms - 1])
					{
						Parse_SourceWarning(source, "too few define parms");
					}
					done = 1;
					break;
				}
			}
			//
			if(numparms < define->numparms)
			{
				//
				t = Parse_CopyToken(&token);
				t->next = NULL;
				if(last)
					last->next = t;
				else
					parms[numparms] = t;
				last = t;
			}
		}
		numparms++;
	}
	return qtrue;
}

/*
===============
Parse_StringizeTokens
===============
*/
static int Parse_StringizeTokens(token_t * tokens, token_t * token)
{
	token_t        *t;

	token->type = TT_STRING;
	token->whitespace_p = NULL;
	token->endwhitespace_p = NULL;
	token->string[0] = '\0';
	strcat(token->string, "\"");
	for(t = tokens; t; t = t->next)
	{
		strncat(token->string, t->string, MAX_TOKEN_CHARS - strlen(token->string));
	}
	strncat(token->string, "\"", MAX_TOKEN_CHARS - strlen(token->string));
	return qtrue;
}

/*
===============
Parse_MergeTokens
===============
*/
static int Parse_MergeTokens(token_t * t1, token_t * t2)
{
	//merging of a name with a name or number
	if(t1->type == TT_NAME && (t2->type == TT_NAME || t2->type == TT_NUMBER))
	{
		strcat(t1->string, t2->string);
		return qtrue;
	}
	//merging of two strings
	if(t1->type == TT_STRING && t2->type == TT_STRING)
	{
		//remove trailing double quote
		t1->string[strlen(t1->string) - 1] = '\0';
		//concat without leading double quote
		strcat(t1->string, &t2->string[1]);
		return qtrue;
	}
	//FIXME: merging of two number of the same sub type
	return qfalse;
}

/*
===============
Parse_NameHash
===============
*/
//char primes[16] = {1, 3, 5, 7, 11, 13, 17, 19, 23, 27, 29, 31, 37, 41, 43, 47};
static int Parse_NameHash(char *name)
{
	int register    hash, i;

	hash = 0;
	for(i = 0; name[i] != '\0'; i++)
	{
		hash += name[i] * (119 + i);
		//hash += (name[i] << 7) + i;
		//hash += (name[i] << (i&15));
	}
	hash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (DEFINEHASHSIZE - 1);
	return hash;
}

/*
===============
Parse_AddDefineToHash
===============
*/
static void Parse_AddDefineToHash(define_t * define, define_t ** definehash)
{
	int             hash;

	hash = Parse_NameHash(define->name);
	define->hashnext = definehash[hash];
	definehash[hash] = define;
}

/*
===============
Parse_FindHashedDefine
===============
*/
static define_t *Parse_FindHashedDefine(define_t ** definehash, char *name)
{
	define_t       *d;
	int             hash;

	hash = Parse_NameHash(name);
	for(d = definehash[hash]; d; d = d->hashnext)
	{
		if(!strcmp(d->name, name))
			return d;
	}
	return NULL;
}

/*
===============
Parse_FindDefineParm
===============
*/
static int Parse_FindDefineParm(define_t * define, char *name)
{
	token_t        *p;
	int             i;

	i = 0;
	for(p = define->parms; p; p = p->next)
	{
		if(!strcmp(p->string, name))
			return i;
		i++;
	}
	return -1;
}

/*
===============
Parse_FreeDefine
===============
*/
static void Parse_FreeDefine(define_t * define)
{
	token_t        *t, *next;

	//free the define parameters
	for(t = define->parms; t; t = next)
	{
		next = t->next;
		Parse_FreeToken(t);
	}
	//free the define tokens
	for(t = define->tokens; t; t = next)
	{
		next = t->next;
		Parse_FreeToken(t);
	}
	//free the define
	Z_Free(define);
}

/*
===============
Parse_ExpandBuiltinDefine
===============
*/
static int Parse_ExpandBuiltinDefine(source_t * source, token_t * deftoken, define_t * define,
									 token_t ** firsttoken, token_t ** lasttoken)
{
	token_t        *token;
	time_t          t;

	char           *curtime;

	token = Parse_CopyToken(deftoken);
	switch (define->builtin)
	{
		case BUILTIN_LINE:
		{
			sprintf(token->string, "%d", deftoken->line);
			token->intvalue = deftoken->line;
			token->floatvalue = deftoken->line;
			token->type = TT_NUMBER;
			token->subtype = TT_DECIMAL | TT_INTEGER;
			*firsttoken = token;
			*lasttoken = token;
			break;
		}
		case BUILTIN_FILE:
		{
			strcpy(token->string, source->scriptstack->filename);
			token->type = TT_NAME;
			token->subtype = strlen(token->string);
			*firsttoken = token;
			*lasttoken = token;
			break;
		}
		case BUILTIN_DATE:
		{
			t = time(NULL);
			curtime = ctime(&t);
			strcpy(token->string, "\"");
			strncat(token->string, curtime + 4, 7);
			strncat(token->string + 7, curtime + 20, 4);
			strcat(token->string, "\"");
			free(curtime);
			token->type = TT_NAME;
			token->subtype = strlen(token->string);
			*firsttoken = token;
			*lasttoken = token;
			break;
		}
		case BUILTIN_TIME:
		{
			t = time(NULL);
			curtime = ctime(&t);
			strcpy(token->string, "\"");
			strncat(token->string, curtime + 11, 8);
			strcat(token->string, "\"");
			free(curtime);
			token->type = TT_NAME;
			token->subtype = strlen(token->string);
			*firsttoken = token;
			*lasttoken = token;
			break;
		}
		case BUILTIN_STDC:
		default:
		{
			*firsttoken = NULL;
			*lasttoken = NULL;
			break;
		}
	}
	return qtrue;
}

/*
===============
Parse_ExpandDefine
===============
*/
static int Parse_ExpandDefine(source_t * source, token_t * deftoken, define_t * define,
							  token_t ** firsttoken, token_t ** lasttoken)
{
	token_t        *parms[MAX_DEFINEPARMS], *dt, *pt, *t;
	token_t        *t1, *t2, *first, *last, *nextpt, token;
	int             parmnum, i;

	//if it is a builtin define
	if(define->builtin)
	{
		return Parse_ExpandBuiltinDefine(source, deftoken, define, firsttoken, lasttoken);
	}
	//if the define has parameters
	if(define->numparms)
	{
		if(!Parse_ReadDefineParms(source, define, parms, MAX_DEFINEPARMS))
			return qfalse;
	}
	//empty list at first
	first = NULL;
	last = NULL;
	//create a list with tokens of the expanded define
	for(dt = define->tokens; dt; dt = dt->next)
	{
		parmnum = -1;
		//if the token is a name, it could be a define parameter
		if(dt->type == TT_NAME)
		{
			parmnum = Parse_FindDefineParm(define, dt->string);
		}
		//if it is a define parameter
		if(parmnum >= 0)
		{
			for(pt = parms[parmnum]; pt; pt = pt->next)
			{
				t = Parse_CopyToken(pt);
				//add the token to the list
				t->next = NULL;
				if(last)
					last->next = t;
				else
					first = t;
				last = t;
			}
		}
		else
		{
			//if stringizing operator
			if(dt->string[0] == '#' && dt->string[1] == '\0')
			{
				//the stringizing operator must be followed by a define parameter
				if(dt->next)
					parmnum = Parse_FindDefineParm(define, dt->next->string);
				else
					parmnum = -1;
				//
				if(parmnum >= 0)
				{
					//step over the stringizing operator
					dt = dt->next;
					//stringize the define parameter tokens
					if(!Parse_StringizeTokens(parms[parmnum], &token))
					{
						Parse_SourceError(source, "can't stringize tokens");
						return qfalse;
					}
					t = Parse_CopyToken(&token);
				}
				else
				{
					Parse_SourceWarning(source, "stringizing operator without define parameter");
					continue;
				}
			}
			else
			{
				t = Parse_CopyToken(dt);
			}
			//add the token to the list
			t->next = NULL;
			if(last)
				last->next = t;
			else
				first = t;
			last = t;
		}
	}
	//check for the merging operator
	for(t = first; t;)
	{
		if(t->next)
		{
			//if the merging operator
			if(t->next->string[0] == '#' && t->next->string[1] == '#')
			{
				t1 = t;
				t2 = t->next->next;
				if(t2)
				{
					if(!Parse_MergeTokens(t1, t2))
					{
						Parse_SourceError(source, "can't merge %s with %s", t1->string, t2->string);
						return qfalse;
					}
					Parse_FreeToken(t1->next);
					t1->next = t2->next;
					if(t2 == last)
						last = t1;
					Parse_FreeToken(t2);
					continue;
				}
			}
		}
		t = t->next;
	}
	//store the first and last token of the list
	*firsttoken = first;
	*lasttoken = last;
	//free all the parameter tokens
	for(i = 0; i < define->numparms; i++)
	{
		for(pt = parms[i]; pt; pt = nextpt)
		{
			nextpt = pt->next;
			Parse_FreeToken(pt);
		}
	}
	//
	return qtrue;
}

/*
===============
Parse_ExpandDefineIntoSource
===============
*/
static int Parse_ExpandDefineIntoSource(source_t * source, token_t * deftoken, define_t * define)
{
	token_t        *firsttoken, *lasttoken;

	if(!Parse_ExpandDefine(source, deftoken, define, &firsttoken, &lasttoken))
		return qfalse;

	if(firsttoken && lasttoken)
	{
		lasttoken->next = source->tokens;
		source->tokens = firsttoken;
		return qtrue;
	}
	return qfalse;
}

/*
===============
Parse_ConvertPath
===============
*/
static void Parse_ConvertPath(char *path)
{
	char           *ptr;

	//remove double path seperators
	for(ptr = path; *ptr;)
	{
		if((*ptr == '\\' || *ptr == '/') && (*(ptr + 1) == '\\' || *(ptr + 1) == '/'))
		{
			strcpy(ptr, ptr + 1);
		}
		else
		{
			ptr++;
		}
	}
	//set OS dependent path seperators
	for(ptr = path; *ptr;)
	{
		if(*ptr == '/' || *ptr == '\\')
			*ptr = PATH_SEP;
		ptr++;
	}
}

/*
===============
Parse_ReadLine

reads a token from the current line, continues reading on the next
line only if a backslash '\' is encountered.
===============
*/
static int Parse_ReadLine(source_t * source, token_t * token)
{
	int             crossline;

	crossline = 0;
	do
	{
		if(!Parse_ReadSourceToken(source, token))
			return qfalse;

		if(token->linescrossed > crossline)
		{
			Parse_UnreadSourceToken(source, token);
			return qfalse;
		}
		crossline = 1;
	} while(!strcmp(token->string, "\\"));
	return qtrue;
}

/*
===============
Parse_OperatorPriority
===============
*/
typedef struct operator_s
{
	int             operator;
	int             priority;
	int             parentheses;
	struct operator_s *prev, *next;
} operator_t;

typedef struct value_s
{
	signed long int intvalue;
	double          floatvalue;
	int             parentheses;
	struct value_s *prev, *next;
} value_t;

static int Parse_OperatorPriority(int op)
{
	switch (op)
	{
		case P_MUL:
			return 15;
		case P_DIV:
			return 15;
		case P_MOD:
			return 15;
		case P_ADD:
			return 14;
		case P_SUB:
			return 14;

		case P_LOGIC_AND:
			return 7;
		case P_LOGIC_OR:
			return 6;
		case P_LOGIC_GEQ:
			return 12;
		case P_LOGIC_LEQ:
			return 12;
		case P_LOGIC_EQ:
			return 11;
		case P_LOGIC_UNEQ:
			return 11;

		case P_LOGIC_NOT:
			return 16;
		case P_LOGIC_GREATER:
			return 12;
		case P_LOGIC_LESS:
			return 12;

		case P_RSHIFT:
			return 13;
		case P_LSHIFT:
			return 13;

		case P_BIN_AND:
			return 10;
		case P_BIN_OR:
			return 8;
		case P_BIN_XOR:
			return 9;
		case P_BIN_NOT:
			return 16;

		case P_COLON:
			return 5;
		case P_QUESTIONMARK:
			return 5;
	}
	return qfalse;
}

#define MAX_VALUES    64
#define MAX_OPERATORS 64
#define AllocValue(val)                 \
  if (numvalues >= MAX_VALUES) {            \
    Parse_SourceError(source, "out of value space\n");    \
    error = 1;                    \
    break;                      \
  }                         \
  else                        \
    val = &value_heap[numvalues++];
#define FreeValue(val)
//
#define AllocOperator(op)               \
  if (numoperators >= MAX_OPERATORS) {        \
    Parse_SourceError(source, "out of operator space\n"); \
    error = 1;                    \
    break;                      \
  }                         \
  else                        \
    op = &operator_heap[numoperators++];
#define FreeOperator(op)

/*
===============
Parse_EvaluateTokens
===============
*/
static int Parse_EvaluateTokens(source_t * source, token_t * tokens, signed long int *intvalue, double *floatvalue, int integer)
{
	operator_t     *o, *firstoperator, *lastoperator;
	value_t        *v, *firstvalue, *lastvalue, *v1, *v2;
	token_t        *t;
	int             brace = 0;
	int             parentheses = 0;
	int             error = 0;
	int             lastwasvalue = 0;
	int             negativevalue = 0;
	int             questmarkintvalue = 0;
	double          questmarkfloatvalue = 0;
	int             gotquestmarkvalue = qfalse;
	int             lastoperatortype = 0;

	//
	operator_t      operator_heap[MAX_OPERATORS];
	int             numoperators = 0;
	value_t         value_heap[MAX_VALUES];
	int             numvalues = 0;

	firstoperator = lastoperator = NULL;
	firstvalue = lastvalue = NULL;
	if(intvalue)
		*intvalue = 0;
	if(floatvalue)
		*floatvalue = 0;
	for(t = tokens; t; t = t->next)
	{
		switch (t->type)
		{
			case TT_NAME:
			{
				if(lastwasvalue || negativevalue)
				{
					Parse_SourceError(source, "syntax error in #if/#elif");
					error = 1;
					break;
				}
				if(strcmp(t->string, "defined"))
				{
					Parse_SourceError(source, "undefined name %s in #if/#elif", t->string);
					error = 1;
					break;
				}
				t = t->next;
				if(!strcmp(t->string, "("))
				{
					brace = qtrue;
					t = t->next;
				}
				if(!t || t->type != TT_NAME)
				{
					Parse_SourceError(source, "defined without name in #if/#elif");
					error = 1;
					break;
				}
				//v = (value_t *) Z_Malloc(sizeof(value_t));
				AllocValue(v);
				if(Parse_FindHashedDefine(source->definehash, t->string))
				{
					v->intvalue = 1;
					v->floatvalue = 1;
				}
				else
				{
					v->intvalue = 0;
					v->floatvalue = 0;
				}
				v->parentheses = parentheses;
				v->next = NULL;
				v->prev = lastvalue;
				if(lastvalue)
					lastvalue->next = v;
				else
					firstvalue = v;
				lastvalue = v;
				if(brace)
				{
					t = t->next;
					if(!t || strcmp(t->string, ")"))
					{
						Parse_SourceError(source, "defined without ) in #if/#elif");
						error = 1;
						break;
					}
				}
				brace = qfalse;
				// defined() creates a value
				lastwasvalue = 1;
				break;
			}
			case TT_NUMBER:
			{
				if(lastwasvalue)
				{
					Parse_SourceError(source, "syntax error in #if/#elif");
					error = 1;
					break;
				}
				//v = (value_t *) Z_Malloc(sizeof(value_t));
				AllocValue(v);
				if(negativevalue)
				{
					v->intvalue = -(signed int)t->intvalue;
					v->floatvalue = -t->floatvalue;
				}
				else
				{
					v->intvalue = t->intvalue;
					v->floatvalue = t->floatvalue;
				}
				v->parentheses = parentheses;
				v->next = NULL;
				v->prev = lastvalue;
				if(lastvalue)
					lastvalue->next = v;
				else
					firstvalue = v;
				lastvalue = v;
				//last token was a value
				lastwasvalue = 1;
				//
				negativevalue = 0;
				break;
			}
			case TT_PUNCTUATION:
			{
				if(negativevalue)
				{
					Parse_SourceError(source, "misplaced minus sign in #if/#elif");
					error = 1;
					break;
				}
				if(t->subtype == P_PARENTHESESOPEN)
				{
					parentheses++;
					break;
				}
				else if(t->subtype == P_PARENTHESESCLOSE)
				{
					parentheses--;
					if(parentheses < 0)
					{
						Parse_SourceError(source, "too many ) in #if/#elsif");
						error = 1;
					}
					break;
				}
				//check for invalid operators on floating point values
				if(!integer)
				{
					if(t->subtype == P_BIN_NOT || t->subtype == P_MOD ||
					   t->subtype == P_RSHIFT || t->subtype == P_LSHIFT ||
					   t->subtype == P_BIN_AND || t->subtype == P_BIN_OR || t->subtype == P_BIN_XOR)
					{
						Parse_SourceError(source, "illigal operator %s on floating point operands\n", t->string);
						error = 1;
						break;
					}
				}
				switch (t->subtype)
				{
					case P_LOGIC_NOT:
					case P_BIN_NOT:
					{
						if(lastwasvalue)
						{
							Parse_SourceError(source, "! or ~ after value in #if/#elif");
							error = 1;
							break;
						}
						break;
					}
					case P_INC:
					case P_DEC:
					{
						Parse_SourceError(source, "++ or -- used in #if/#elif");
						break;
					}
					case P_SUB:
					{
						if(!lastwasvalue)
						{
							negativevalue = 1;
							break;
						}
					}

					case P_MUL:
					case P_DIV:
					case P_MOD:
					case P_ADD:

					case P_LOGIC_AND:
					case P_LOGIC_OR:
					case P_LOGIC_GEQ:
					case P_LOGIC_LEQ:
					case P_LOGIC_EQ:
					case P_LOGIC_UNEQ:

					case P_LOGIC_GREATER:
					case P_LOGIC_LESS:

					case P_RSHIFT:
					case P_LSHIFT:

					case P_BIN_AND:
					case P_BIN_OR:
					case P_BIN_XOR:

					case P_COLON:
					case P_QUESTIONMARK:
					{
						if(!lastwasvalue)
						{
							Parse_SourceError(source, "operator %s after operator in #if/#elif", t->string);
							error = 1;
							break;
						}
						break;
					}
					default:
					{
						Parse_SourceError(source, "invalid operator %s in #if/#elif", t->string);
						error = 1;
						break;
					}
				}
				if(!error && !negativevalue)
				{
					//o = (operator_t *) Z_Malloc(sizeof(operator_t));
					AllocOperator(o);
					o->operator = t->subtype;
					o->priority = Parse_OperatorPriority(t->subtype);
					o->parentheses = parentheses;
					o->next = NULL;
					o->prev = lastoperator;
					if(lastoperator)
						lastoperator->next = o;
					else
						firstoperator = o;
					lastoperator = o;
					lastwasvalue = 0;
				}
				break;
			}
			default:
			{
				Parse_SourceError(source, "unknown %s in #if/#elif", t->string);
				error = 1;
				break;
			}
		}
		if(error)
			break;
	}
	if(!error)
	{
		if(!lastwasvalue)
		{
			Parse_SourceError(source, "trailing operator in #if/#elif");
			error = 1;
		}
		else if(parentheses)
		{
			Parse_SourceError(source, "too many ( in #if/#elif");
			error = 1;
		}
	}
	//
	gotquestmarkvalue = qfalse;
	questmarkintvalue = 0;
	questmarkfloatvalue = 0;
	//while there are operators
	while(!error && firstoperator)
	{
		v = firstvalue;
		for(o = firstoperator; o->next; o = o->next)
		{
			//if the current operator is nested deeper in parentheses
			//than the next operator
			if(o->parentheses > o->next->parentheses)
				break;
			//if the current and next operator are nested equally deep in parentheses
			if(o->parentheses == o->next->parentheses)
			{
				//if the priority of the current operator is equal or higher
				//than the priority of the next operator
				if(o->priority >= o->next->priority)
					break;
			}
			//if the arity of the operator isn't equal to 1
			if(o->operator != P_LOGIC_NOT && o->operator != P_BIN_NOT)
				v = v->next;
			//if there's no value or no next value
			if(!v)
			{
				Parse_SourceError(source, "mising values in #if/#elif");
				error = 1;
				break;
			}
		}
		if(error)
			break;
		v1 = v;
		v2 = v->next;
		switch (o->operator)
		{
			case P_LOGIC_NOT:
				v1->intvalue = !v1->intvalue;
				v1->floatvalue = !v1->floatvalue;
				break;
			case P_BIN_NOT:
				v1->intvalue = ~v1->intvalue;
				break;
			case P_MUL:
				v1->intvalue *= v2->intvalue;
				v1->floatvalue *= v2->floatvalue;
				break;
			case P_DIV:
				if(!v2->intvalue || !v2->floatvalue)
				{
					Parse_SourceError(source, "divide by zero in #if/#elif\n");
					error = 1;
					break;
				}
				v1->intvalue /= v2->intvalue;
				v1->floatvalue /= v2->floatvalue;
				break;
			case P_MOD:
				if(!v2->intvalue)
				{
					Parse_SourceError(source, "divide by zero in #if/#elif\n");
					error = 1;
					break;
				}
				v1->intvalue %= v2->intvalue;
				break;
			case P_ADD:
				v1->intvalue += v2->intvalue;
				v1->floatvalue += v2->floatvalue;
				break;
			case P_SUB:
				v1->intvalue -= v2->intvalue;
				v1->floatvalue -= v2->floatvalue;
				break;
			case P_LOGIC_AND:
				v1->intvalue = v1->intvalue && v2->intvalue;
				v1->floatvalue = v1->floatvalue && v2->floatvalue;
				break;
			case P_LOGIC_OR:
				v1->intvalue = v1->intvalue || v2->intvalue;
				v1->floatvalue = v1->floatvalue || v2->floatvalue;
				break;
			case P_LOGIC_GEQ:
				v1->intvalue = v1->intvalue >= v2->intvalue;
				v1->floatvalue = v1->floatvalue >= v2->floatvalue;
				break;
			case P_LOGIC_LEQ:
				v1->intvalue = v1->intvalue <= v2->intvalue;
				v1->floatvalue = v1->floatvalue <= v2->floatvalue;
				break;
			case P_LOGIC_EQ:
				v1->intvalue = v1->intvalue == v2->intvalue;
				v1->floatvalue = v1->floatvalue == v2->floatvalue;
				break;
			case P_LOGIC_UNEQ:
				v1->intvalue = v1->intvalue != v2->intvalue;
				v1->floatvalue = v1->floatvalue != v2->floatvalue;
				break;
			case P_LOGIC_GREATER:
				v1->intvalue = v1->intvalue > v2->intvalue;
				v1->floatvalue = v1->floatvalue > v2->floatvalue;
				break;
			case P_LOGIC_LESS:
				v1->intvalue = v1->intvalue < v2->intvalue;
				v1->floatvalue = v1->floatvalue < v2->floatvalue;
				break;
			case P_RSHIFT:
				v1->intvalue >>= v2->intvalue;
				break;
			case P_LSHIFT:
				v1->intvalue <<= v2->intvalue;
				break;
			case P_BIN_AND:
				v1->intvalue &= v2->intvalue;
				break;
			case P_BIN_OR:
				v1->intvalue |= v2->intvalue;
				break;
			case P_BIN_XOR:
				v1->intvalue ^= v2->intvalue;
				break;
			case P_COLON:
			{
				if(!gotquestmarkvalue)
				{
					Parse_SourceError(source, ": without ? in #if/#elif");
					error = 1;
					break;
				}
				if(integer)
				{
					if(!questmarkintvalue)
						v1->intvalue = v2->intvalue;
				}
				else
				{
					if(!questmarkfloatvalue)
						v1->floatvalue = v2->floatvalue;
				}
				gotquestmarkvalue = qfalse;
				break;
			}
			case P_QUESTIONMARK:
			{
				if(gotquestmarkvalue)
				{
					Parse_SourceError(source, "? after ? in #if/#elif");
					error = 1;
					break;
				}
				questmarkintvalue = v1->intvalue;
				questmarkfloatvalue = v1->floatvalue;
				gotquestmarkvalue = qtrue;
				break;
			}
		}
		if(error)
			break;
		lastoperatortype = o->operator;
		//if not an operator with arity 1
		if(o->operator != P_LOGIC_NOT && o->operator != P_BIN_NOT)
		{
			//remove the second value if not question mark operator
			if(o->operator != P_QUESTIONMARK)
				v = v->next;
			//
			if(v->prev)
				v->prev->next = v->next;
			else
				firstvalue = v->next;
			if(v->next)
				v->next->prev = v->prev;
			else
				lastvalue = v->prev;
			//Z_Free(v);
			FreeValue(v);
		}
		//remove the operator
		if(o->prev)
			o->prev->next = o->next;
		else
			firstoperator = o->next;
		if(o->next)
			o->next->prev = o->prev;
		else
			lastoperator = o->prev;
		//Z_Free(o);
		FreeOperator(o);
	}
	if(firstvalue)
	{
		if(intvalue)
			*intvalue = firstvalue->intvalue;
		if(floatvalue)
			*floatvalue = firstvalue->floatvalue;
	}
	for(o = firstoperator; o; o = lastoperator)
	{
		lastoperator = o->next;
		//Z_Free(o);
		FreeOperator(o);
	}
	for(v = firstvalue; v; v = lastvalue)
	{
		lastvalue = v->next;
		//Z_Free(v);
		FreeValue(v);
	}
	if(!error)
		return qtrue;
	if(intvalue)
		*intvalue = 0;
	if(floatvalue)
		*floatvalue = 0;
	return qfalse;
}

/*
===============
Parse_Evaluate
===============
*/
static int Parse_Evaluate(source_t * source, signed long int *intvalue, double *floatvalue, int integer)
{
	token_t         token, *firsttoken, *lasttoken;
	token_t        *t, *nexttoken;
	define_t       *define;
	int             defined = qfalse;

	if(intvalue)
		*intvalue = 0;
	if(floatvalue)
		*floatvalue = 0;
	//
	if(!Parse_ReadLine(source, &token))
	{
		Parse_SourceError(source, "no value after #if/#elif");
		return qfalse;
	}
	firsttoken = NULL;
	lasttoken = NULL;
	do
	{
		//if the token is a name
		if(token.type == TT_NAME)
		{
			if(defined)
			{
				defined = qfalse;
				t = Parse_CopyToken(&token);
				t->next = NULL;
				if(lasttoken)
					lasttoken->next = t;
				else
					firsttoken = t;
				lasttoken = t;
			}
			else if(!strcmp(token.string, "defined"))
			{
				defined = qtrue;
				t = Parse_CopyToken(&token);
				t->next = NULL;
				if(lasttoken)
					lasttoken->next = t;
				else
					firsttoken = t;
				lasttoken = t;
			}
			else
			{
				//then it must be a define
				define = Parse_FindHashedDefine(source->definehash, token.string);
				if(!define)
				{
					Parse_SourceError(source, "can't evaluate %s, not defined", token.string);
					return qfalse;
				}
				if(!Parse_ExpandDefineIntoSource(source, &token, define))
					return qfalse;
			}
		}
		//if the token is a number or a punctuation
		else if(token.type == TT_NUMBER || token.type == TT_PUNCTUATION)
		{
			t = Parse_CopyToken(&token);
			t->next = NULL;
			if(lasttoken)
				lasttoken->next = t;
			else
				firsttoken = t;
			lasttoken = t;
		}
		else					//can't evaluate the token
		{
			Parse_SourceError(source, "can't evaluate %s", token.string);
			return qfalse;
		}
	} while(Parse_ReadLine(source, &token));
	//
	if(!Parse_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer))
		return qfalse;
	//
	for(t = firsttoken; t; t = nexttoken)
	{
		nexttoken = t->next;
		Parse_FreeToken(t);
	}
	//
	return qtrue;
}

/*
===============
Parse_DollarEvaluate
===============
*/
static int Parse_DollarEvaluate(source_t * source, signed long int *intvalue, double *floatvalue, int integer)
{
	int             indent, defined = qfalse;
	token_t         token, *firsttoken, *lasttoken;
	token_t        *t, *nexttoken;
	define_t       *define;

	if(intvalue)
		*intvalue = 0;
	if(floatvalue)
		*floatvalue = 0;
	//
	if(!Parse_ReadSourceToken(source, &token))
	{
		Parse_SourceError(source, "no leading ( after $evalint/$evalfloat");
		return qfalse;
	}
	if(!Parse_ReadSourceToken(source, &token))
	{
		Parse_SourceError(source, "nothing to evaluate");
		return qfalse;
	}
	indent = 1;
	firsttoken = NULL;
	lasttoken = NULL;
	do
	{
		//if the token is a name
		if(token.type == TT_NAME)
		{
			if(defined)
			{
				defined = qfalse;
				t = Parse_CopyToken(&token);
				t->next = NULL;
				if(lasttoken)
					lasttoken->next = t;
				else
					firsttoken = t;
				lasttoken = t;
			}
			else if(!strcmp(token.string, "defined"))
			{
				defined = qtrue;
				t = Parse_CopyToken(&token);
				t->next = NULL;
				if(lasttoken)
					lasttoken->next = t;
				else
					firsttoken = t;
				lasttoken = t;
			}
			else
			{
				//then it must be a define
				define = Parse_FindHashedDefine(source->definehash, token.string);
				if(!define)
				{
					Parse_SourceError(source, "can't evaluate %s, not defined", token.string);
					return qfalse;
				}
				if(!Parse_ExpandDefineIntoSource(source, &token, define))
					return qfalse;
			}
		}
		//if the token is a number or a punctuation
		else if(token.type == TT_NUMBER || token.type == TT_PUNCTUATION)
		{
			if(*token.string == '(')
				indent++;
			else if(*token.string == ')')
				indent--;
			if(indent <= 0)
				break;
			t = Parse_CopyToken(&token);
			t->next = NULL;
			if(lasttoken)
				lasttoken->next = t;
			else
				firsttoken = t;
			lasttoken = t;
		}
		else					//can't evaluate the token
		{
			Parse_SourceError(source, "can't evaluate %s", token.string);
			return qfalse;
		}
	} while(Parse_ReadSourceToken(source, &token));
	//
	if(!Parse_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer))
		return qfalse;
	//
	for(t = firsttoken; t; t = nexttoken)
	{
		nexttoken = t->next;
		Parse_FreeToken(t);
	}
	//
	return qtrue;
}

/*
===============
Parse_Directive_include
===============
*/
static int Parse_Directive_include(source_t * source)
{
	script_t       *script;
	token_t         token;
	char            path[MAX_QPATH];

	if(source->skip > 0)
		return qtrue;
	//
	if(!Parse_ReadSourceToken(source, &token))
	{
		Parse_SourceError(source, "#include without file name");
		return qfalse;
	}
	if(token.linescrossed > 0)
	{
		Parse_SourceError(source, "#include without file name");
		return qfalse;
	}
	if(token.type == TT_STRING)
	{
		Parse_StripDoubleQuotes(token.string);
		Parse_ConvertPath(token.string);
		script = Parse_LoadScriptFile(token.string);
		if(!script)
		{
			strcpy(path, source->includepath);
			strcat(path, token.string);
			script = Parse_LoadScriptFile(path);
		}
	}
	else if(token.type == TT_PUNCTUATION && *token.string == '<')
	{
		strcpy(path, source->includepath);
		while(Parse_ReadSourceToken(source, &token))
		{
			if(token.linescrossed > 0)
			{
				Parse_UnreadSourceToken(source, &token);
				break;
			}
			if(token.type == TT_PUNCTUATION && *token.string == '>')
				break;
			Q_strcat(path, MAX_QPATH, token.string);
		}
		if(*token.string != '>')
		{
			Parse_SourceWarning(source, "#include missing trailing >");
		}
		if(!strlen(path))
		{
			Parse_SourceError(source, "#include without file name between < >");
			return qfalse;
		}
		Parse_ConvertPath(path);
		script = Parse_LoadScriptFile(path);
	}
	else
	{
		Parse_SourceError(source, "#include without file name");
		return qfalse;
	}
	if(!script)
	{
		Parse_SourceError(source, "file %s not found", path);
		return qfalse;
	}
	Parse_PushScript(source, script);
	return qtrue;
}

/*
===============
Parse_WhiteSpaceBeforeToken
===============
*/
static int Parse_WhiteSpaceBeforeToken(token_t * token)
{
	return token->endwhitespace_p - token->whitespace_p > 0;
}

/*
===============
Parse_ClearTokenWhiteSpace
===============
*/
static void Parse_ClearTokenWhiteSpace(token_t * token)
{
	token->whitespace_p = NULL;
	token->endwhitespace_p = NULL;
	token->linescrossed = 0;
}

/*
===============
Parse_Directive_undef
===============
*/
static int Parse_Directive_undef(source_t * source)
{
	token_t         token;
	define_t       *define, *lastdefine;
	int             hash;

	if(source->skip > 0)
		return qtrue;
	//
	if(!Parse_ReadLine(source, &token))
	{
		Parse_SourceError(source, "undef without name");
		return qfalse;
	}
	if(token.type != TT_NAME)
	{
		Parse_UnreadSourceToken(source, &token);
		Parse_SourceError(source, "expected name, found %s", token.string);
		return qfalse;
	}

	hash = Parse_NameHash(token.string);
	for(lastdefine = NULL, define = source->definehash[hash]; define; define = define->hashnext)
	{
		if(!strcmp(define->name, token.string))
		{
			if(define->flags & DEFINE_FIXED)
			{
				Parse_SourceWarning(source, "can't undef %s", token.string);
			}
			else
			{
				if(lastdefine)
					lastdefine->hashnext = define->hashnext;
				else
					source->definehash[hash] = define->hashnext;
				Parse_FreeDefine(define);
			}
			break;
		}
		lastdefine = define;
	}
	return qtrue;
}

/*
===============
Parse_Directive_elif
===============
*/
static int Parse_Directive_elif(source_t * source)
{
	signed long int value;
	int             type, skip;

	Parse_PopIndent(source, &type, &skip);
	if(!type || type == INDENT_ELSE)
	{
		Parse_SourceError(source, "misplaced #elif");
		return qfalse;
	}
	if(!Parse_Evaluate(source, &value, NULL, qtrue))
		return qfalse;
	skip = (value == 0);
	Parse_PushIndent(source, INDENT_ELIF, skip);
	return qtrue;
}

/*
===============
Parse_Directive_if
===============
*/
static int Parse_Directive_if(source_t * source)
{
	signed long int value;
	int             skip;

	if(!Parse_Evaluate(source, &value, NULL, qtrue))
		return qfalse;
	skip = (value == 0);
	Parse_PushIndent(source, INDENT_IF, skip);
	return qtrue;
}

/*
===============
Parse_Directive_line
===============
*/
static int Parse_Directive_line(source_t * source)
{
	Parse_SourceError(source, "#line directive not supported");
	return qfalse;
}

/*
===============
Parse_Directive_error
===============
*/
static int Parse_Directive_error(source_t * source)
{
	token_t         token;

	strcpy(token.string, "");
	Parse_ReadSourceToken(source, &token);
	Parse_SourceError(source, "#error directive: %s", token.string);
	return qfalse;
}

/*
===============
Parse_Directive_pragma
===============
*/
static int Parse_Directive_pragma(source_t * source)
{
	token_t         token;

	Parse_SourceWarning(source, "#pragma directive not supported");
	while(Parse_ReadLine(source, &token));
	return qtrue;
}

/*
===============
Parse_UnreadSignToken
===============
*/
static void Parse_UnreadSignToken(source_t * source)
{
	token_t         token;

	token.line = source->scriptstack->line;
	token.whitespace_p = source->scriptstack->script_p;
	token.endwhitespace_p = source->scriptstack->script_p;
	token.linescrossed = 0;
	strcpy(token.string, "-");
	token.type = TT_PUNCTUATION;
	token.subtype = P_SUB;
	Parse_UnreadSourceToken(source, &token);
}

/*
===============
Parse_Directive_eval
===============
*/
static int Parse_Directive_eval(source_t * source)
{
	signed long int value;
	token_t         token;

	if(!Parse_Evaluate(source, &value, NULL, qtrue))
		return qfalse;
	//
	token.line = source->scriptstack->line;
	token.whitespace_p = source->scriptstack->script_p;
	token.endwhitespace_p = source->scriptstack->script_p;
	token.linescrossed = 0;
	sprintf(token.string, "%d", abs(value));
	token.type = TT_NUMBER;
	token.subtype = TT_INTEGER | TT_LONG | TT_DECIMAL;
	Parse_UnreadSourceToken(source, &token);
	if(value < 0)
		Parse_UnreadSignToken(source);
	return qtrue;
}

/*
===============
Parse_Directive_evalfloat
===============
*/
static int Parse_Directive_evalfloat(source_t * source)
{
	double          value;
	token_t         token;

	if(!Parse_Evaluate(source, NULL, &value, qfalse))
		return qfalse;
	token.line = source->scriptstack->line;
	token.whitespace_p = source->scriptstack->script_p;
	token.endwhitespace_p = source->scriptstack->script_p;
	token.linescrossed = 0;
	sprintf(token.string, "%1.2f", fabs(value));
	token.type = TT_NUMBER;
	token.subtype = TT_FLOAT | TT_LONG | TT_DECIMAL;
	Parse_UnreadSourceToken(source, &token);
	if(value < 0)
		Parse_UnreadSignToken(source);
	return qtrue;
}

/*
===============
Parse_DollarDirective_evalint
===============
*/
static int Parse_DollarDirective_evalint(source_t * source)
{
	signed long int value;
	token_t         token;

	if(!Parse_DollarEvaluate(source, &value, NULL, qtrue))
		return qfalse;
	//
	token.line = source->scriptstack->line;
	token.whitespace_p = source->scriptstack->script_p;
	token.endwhitespace_p = source->scriptstack->script_p;
	token.linescrossed = 0;
	sprintf(token.string, "%d", abs(value));
	token.type = TT_NUMBER;
	token.subtype = TT_INTEGER | TT_LONG | TT_DECIMAL;
	token.intvalue = value;
	token.floatvalue = value;
	Parse_UnreadSourceToken(source, &token);
	if(value < 0)
		Parse_UnreadSignToken(source);
	return qtrue;
}

/*
===============
Parse_DollarDirective_evalfloat
===============
*/
static int Parse_DollarDirective_evalfloat(source_t * source)
{
	double          value;
	token_t         token;

	if(!Parse_DollarEvaluate(source, NULL, &value, qfalse))
		return qfalse;
	token.line = source->scriptstack->line;
	token.whitespace_p = source->scriptstack->script_p;
	token.endwhitespace_p = source->scriptstack->script_p;
	token.linescrossed = 0;
	sprintf(token.string, "%1.2f", fabs(value));
	token.type = TT_NUMBER;
	token.subtype = TT_FLOAT | TT_LONG | TT_DECIMAL;
	token.intvalue = (unsigned long)value;
	token.floatvalue = value;
	Parse_UnreadSourceToken(source, &token);
	if(value < 0)
		Parse_UnreadSignToken(source);
	return qtrue;
}

/*
===============
Parse_ReadDollarDirective
===============
*/
directive_t     dollardirectives[20] = {
	{"evalint", Parse_DollarDirective_evalint}
	,
	{"evalfloat", Parse_DollarDirective_evalfloat}
	,
	{NULL, NULL}
};

static int Parse_ReadDollarDirective(source_t * source)
{
	token_t         token;
	int             i;

	//read the directive name
	if(!Parse_ReadSourceToken(source, &token))
	{
		Parse_SourceError(source, "found $ without name");
		return qfalse;
	}
	//directive name must be on the same line
	if(token.linescrossed > 0)
	{
		Parse_UnreadSourceToken(source, &token);
		Parse_SourceError(source, "found $ at end of line");
		return qfalse;
	}
	//if if is a name
	if(token.type == TT_NAME)
	{
		//find the precompiler directive
		for(i = 0; dollardirectives[i].name; i++)
		{
			if(!strcmp(dollardirectives[i].name, token.string))
			{
				return dollardirectives[i].func(source);
			}
		}
	}
	Parse_UnreadSourceToken(source, &token);
	Parse_SourceError(source, "unknown precompiler directive %s", token.string);
	return qfalse;
}

/*
===============
Parse_Directive_if_def
===============
*/
static int Parse_Directive_if_def(source_t * source, int type)
{
	token_t         token;
	define_t       *d;
	int             skip;

	if(!Parse_ReadLine(source, &token))
	{
		Parse_SourceError(source, "#ifdef without name");
		return qfalse;
	}
	if(token.type != TT_NAME)
	{
		Parse_UnreadSourceToken(source, &token);
		Parse_SourceError(source, "expected name after #ifdef, found %s", token.string);
		return qfalse;
	}
	d = Parse_FindHashedDefine(source->definehash, token.string);
	skip = (type == INDENT_IFDEF) == (d == NULL);
	Parse_PushIndent(source, type, skip);
	return qtrue;
}

/*
===============
Parse_Directive_ifdef
===============
*/
static int Parse_Directive_ifdef(source_t * source)
{
	return Parse_Directive_if_def(source, INDENT_IFDEF);
}

/*
===============
Parse_Directive_ifndef
===============
*/
static int Parse_Directive_ifndef(source_t * source)
{
	return Parse_Directive_if_def(source, INDENT_IFNDEF);
}

/*
===============
Parse_Directive_else
===============
*/
static int Parse_Directive_else(source_t * source)
{
	int             type, skip;

	Parse_PopIndent(source, &type, &skip);
	if(!type)
	{
		Parse_SourceError(source, "misplaced #else");
		return qfalse;
	}
	if(type == INDENT_ELSE)
	{
		Parse_SourceError(source, "#else after #else");
		return qfalse;
	}
	Parse_PushIndent(source, INDENT_ELSE, !skip);
	return qtrue;
}

/*
===============
Parse_Directive_endif
===============
*/
static int Parse_Directive_endif(source_t * source)
{
	int             type, skip;

	Parse_PopIndent(source, &type, &skip);
	if(!type)
	{
		Parse_SourceError(source, "misplaced #endif");
		return qfalse;
	}
	return qtrue;
}

/*
===============
Parse_CheckTokenString
===============
*/
static int Parse_CheckTokenString(source_t * source, char *string)
{
	token_t         tok;

	if(!Parse_ReadToken(source, &tok))
		return qfalse;
	//if the token is available
	if(!strcmp(tok.string, string))
		return qtrue;
	//
	Parse_UnreadSourceToken(source, &tok);
	return qfalse;
}

/*
===============
Parse_Directive_define
===============
*/
static int Parse_Directive_define(source_t * source)
{
	token_t         token, *t, *last;
	define_t       *define;

	if(source->skip > 0)
		return qtrue;
	//
	if(!Parse_ReadLine(source, &token))
	{
		Parse_SourceError(source, "#define without name");
		return qfalse;
	}
	if(token.type != TT_NAME)
	{
		Parse_UnreadSourceToken(source, &token);
		Parse_SourceError(source, "expected name after #define, found %s", token.string);
		return qfalse;
	}
	//check if the define already exists
	define = Parse_FindHashedDefine(source->definehash, token.string);
	if(define)
	{
		if(define->flags & DEFINE_FIXED)
		{
			Parse_SourceError(source, "can't redefine %s", token.string);
			return qfalse;
		}
		Parse_SourceWarning(source, "redefinition of %s", token.string);
		//unread the define name before executing the #undef directive
		Parse_UnreadSourceToken(source, &token);
		if(!Parse_Directive_undef(source))
			return qfalse;
		//if the define was not removed (define->flags & DEFINE_FIXED)
		define = Parse_FindHashedDefine(source->definehash, token.string);
	}
	//allocate define
	define = (define_t *) Z_Malloc(sizeof(define_t) + strlen(token.string) + 1);
	Com_Memset(define, 0, sizeof(define_t));
	define->name = (char *)define + sizeof(define_t);
	strcpy(define->name, token.string);
	//add the define to the source
	Parse_AddDefineToHash(define, source->definehash);
	//if nothing is defined, just return
	if(!Parse_ReadLine(source, &token))
		return qtrue;
	//if it is a define with parameters
	if(!Parse_WhiteSpaceBeforeToken(&token) && !strcmp(token.string, "("))
	{
		//read the define parameters
		last = NULL;
		if(!Parse_CheckTokenString(source, ")"))
		{
			while(1)
			{
				if(!Parse_ReadLine(source, &token))
				{
					Parse_SourceError(source, "expected define parameter");
					return qfalse;
				}
				//if it isn't a name
				if(token.type != TT_NAME)
				{
					Parse_SourceError(source, "invalid define parameter");
					return qfalse;
				}
				//
				if(Parse_FindDefineParm(define, token.string) >= 0)
				{
					Parse_SourceError(source, "two the same define parameters");
					return qfalse;
				}
				//add the define parm
				t = Parse_CopyToken(&token);
				Parse_ClearTokenWhiteSpace(t);
				t->next = NULL;
				if(last)
					last->next = t;
				else
					define->parms = t;
				last = t;
				define->numparms++;
				//read next token
				if(!Parse_ReadLine(source, &token))
				{
					Parse_SourceError(source, "define parameters not terminated");
					return qfalse;
				}
				//
				if(!strcmp(token.string, ")"))
					break;
				//then it must be a comma
				if(strcmp(token.string, ","))
				{
					Parse_SourceError(source, "define not terminated");
					return qfalse;
				}
			}
		}
		if(!Parse_ReadLine(source, &token))
			return qtrue;
	}
	//read the defined stuff
	last = NULL;
	do
	{
		t = Parse_CopyToken(&token);
		if(t->type == TT_NAME && !strcmp(t->string, define->name))
		{
			Parse_SourceError(source, "recursive define (removed recursion)");
			continue;
		}
		Parse_ClearTokenWhiteSpace(t);
		t->next = NULL;
		if(last)
			last->next = t;
		else
			define->tokens = t;
		last = t;
	} while(Parse_ReadLine(source, &token));
	//
	if(last)
	{
		//check for merge operators at the beginning or end
		if(!strcmp(define->tokens->string, "##") || !strcmp(last->string, "##"))
		{
			Parse_SourceError(source, "define with misplaced ##");
			return qfalse;
		}
	}
	return qtrue;
}

/*
===============
Parse_ReadDirective
===============
*/
directive_t     directives[20] = {
	{"if", Parse_Directive_if}
	,
	{"ifdef", Parse_Directive_ifdef}
	,
	{"ifndef", Parse_Directive_ifndef}
	,
	{"elif", Parse_Directive_elif}
	,
	{"else", Parse_Directive_else}
	,
	{"endif", Parse_Directive_endif}
	,
	{"include", Parse_Directive_include}
	,
	{"define", Parse_Directive_define}
	,
	{"undef", Parse_Directive_undef}
	,
	{"line", Parse_Directive_line}
	,
	{"error", Parse_Directive_error}
	,
	{"pragma", Parse_Directive_pragma}
	,
	{"eval", Parse_Directive_eval}
	,
	{"evalfloat", Parse_Directive_evalfloat}
	,
	{NULL, NULL}
};

static int Parse_ReadDirective(source_t * source)
{
	token_t         token;
	int             i;

	//read the directive name
	if(!Parse_ReadSourceToken(source, &token))
	{
		Parse_SourceError(source, "found # without name");
		return qfalse;
	}
	//directive name must be on the same line
	if(token.linescrossed > 0)
	{
		Parse_UnreadSourceToken(source, &token);
		Parse_SourceError(source, "found # at end of line");
		return qfalse;
	}
	//if if is a name
	if(token.type == TT_NAME)
	{
		//find the precompiler directive
		for(i = 0; directives[i].name; i++)
		{
			if(!strcmp(directives[i].name, token.string))
			{
				return directives[i].func(source);
			}
		}
	}
	Parse_SourceError(source, "unknown precompiler directive %s", token.string);
	return qfalse;
}

/*
===============
Parse_UnreadToken
===============
*/
static void Parse_UnreadToken(source_t * source, token_t * token)
{
	Parse_UnreadSourceToken(source, token);
}

/*
===============
Parse_ReadToken
===============
*/
static int Parse_ReadToken(source_t * source, token_t * token)
{
	define_t       *define;

	while(1)
	{
		if(!Parse_ReadSourceToken(source, token))
			return qfalse;
		//check for precompiler directives
		if(token->type == TT_PUNCTUATION && *token->string == '#')
		{
			{
				//read the precompiler directive
				if(!Parse_ReadDirective(source))
					return qfalse;
				continue;
			}
		}
		if(token->type == TT_PUNCTUATION && *token->string == '$')
		{
			{
				//read the precompiler directive
				if(!Parse_ReadDollarDirective(source))
					return qfalse;
				continue;
			}
		}
		// recursively concatenate strings that are behind each other still resolving defines
		if(token->type == TT_STRING)
		{
			token_t         newtoken;

			if(Parse_ReadToken(source, &newtoken))
			{
				if(newtoken.type == TT_STRING)
				{
					token->string[strlen(token->string) - 1] = '\0';
					if(strlen(token->string) + strlen(newtoken.string + 1) + 1 >= MAX_TOKEN_CHARS)
					{
						Parse_SourceError(source, "string longer than MAX_TOKEN_CHARS %d\n", MAX_TOKEN_CHARS);
						return qfalse;
					}
					strcat(token->string, newtoken.string + 1);
				}
				else
				{
					Parse_UnreadToken(source, &newtoken);
				}
			}
		}
		//if skipping source because of conditional compilation
		if(source->skip)
			continue;
		//if the token is a name
		if(token->type == TT_NAME)
		{
			//check if the name is a define macro
			define = Parse_FindHashedDefine(source->definehash, token->string);
			//if it is a define macro
			if(define)
			{
				//expand the defined macro
				if(!Parse_ExpandDefineIntoSource(source, token, define))
					return qfalse;
				continue;
			}
		}
		//copy token for unreading
		Com_Memcpy(&source->token, token, sizeof(token_t));
		//found a token
		return qtrue;
	}
}

/*
===============
Parse_DefineFromString
===============
*/
static define_t *Parse_DefineFromString(char *string)
{
	script_t       *script;
	source_t        src;
	token_t        *t;
	int             res, i;
	define_t       *def;

	script = Parse_LoadScriptMemory(string, strlen(string), "*extern");
	//create a new source
	Com_Memset(&src, 0, sizeof(source_t));
	strncpy(src.filename, "*extern", MAX_QPATH);
	src.scriptstack = script;
	src.definehash = Z_Malloc(DEFINEHASHSIZE * sizeof(define_t *));
	Com_Memset(src.definehash, 0, DEFINEHASHSIZE * sizeof(define_t *));
	//create a define from the source
	res = Parse_Directive_define(&src);
	//free any tokens if left
	for(t = src.tokens; t; t = src.tokens)
	{
		src.tokens = src.tokens->next;
		Parse_FreeToken(t);
	}
	def = NULL;
	for(i = 0; i < DEFINEHASHSIZE; i++)
	{
		if(src.definehash[i])
		{
			def = src.definehash[i];
			break;
		}
	}
	//
	Z_Free(src.definehash);
	//
	Parse_FreeScript(script);
	//if the define was created succesfully
	if(res > 0)
		return def;
	//free the define is created
	if(src.defines)
		Parse_FreeDefine(def);
	//
	return NULL;
}

/*
===============
Parse_AddGlobalDefine

add a globals define that will be added to all opened sources
===============
*/
int Parse_AddGlobalDefine(char *string)
{
	define_t       *define;

	define = Parse_DefineFromString(string);
	if(!define)
		return qfalse;
	define->next = globaldefines;
	globaldefines = define;
	return qtrue;
}

/*
===============
Parse_CopyDefine
===============
*/
static define_t *Parse_CopyDefine(source_t * source, define_t * define)
{
	define_t       *newdefine;
	token_t        *token, *newtoken, *lasttoken;

	newdefine = (define_t *) Z_Malloc(sizeof(define_t) + strlen(define->name) + 1);
	//copy the define name
	newdefine->name = (char *)newdefine + sizeof(define_t);
	strcpy(newdefine->name, define->name);
	newdefine->flags = define->flags;
	newdefine->builtin = define->builtin;
	newdefine->numparms = define->numparms;
	//the define is not linked
	newdefine->next = NULL;
	newdefine->hashnext = NULL;
	//copy the define tokens
	newdefine->tokens = NULL;
	for(lasttoken = NULL, token = define->tokens; token; token = token->next)
	{
		newtoken = Parse_CopyToken(token);
		newtoken->next = NULL;
		if(lasttoken)
			lasttoken->next = newtoken;
		else
			newdefine->tokens = newtoken;
		lasttoken = newtoken;
	}
	//copy the define parameters
	newdefine->parms = NULL;
	for(lasttoken = NULL, token = define->parms; token; token = token->next)
	{
		newtoken = Parse_CopyToken(token);
		newtoken->next = NULL;
		if(lasttoken)
			lasttoken->next = newtoken;
		else
			newdefine->parms = newtoken;
		lasttoken = newtoken;
	}
	return newdefine;
}

/*
===============
Parse_AddGlobalDefinesToSource
===============
*/
static void Parse_AddGlobalDefinesToSource(source_t * source)
{
	define_t       *define, *newdefine;

	for(define = globaldefines; define; define = define->next)
	{
		newdefine = Parse_CopyDefine(source, define);
		Parse_AddDefineToHash(newdefine, source->definehash);
	}
}

/*
===============
Parse_LoadSourceFile
===============
*/
static source_t *Parse_LoadSourceFile(const char *filename)
{
	source_t       *source;
	script_t       *script;

	script = Parse_LoadScriptFile(filename);
	if(!script)
		return NULL;

	script->next = NULL;

	source = (source_t *) Z_Malloc(sizeof(source_t));
	Com_Memset(source, 0, sizeof(source_t));

	strncpy(source->filename, filename, MAX_QPATH);
	source->scriptstack = script;
	source->tokens = NULL;
	source->defines = NULL;
	source->indentstack = NULL;
	source->skip = 0;

	source->definehash = Z_Malloc(DEFINEHASHSIZE * sizeof(define_t *));
	Com_Memset(source->definehash, 0, DEFINEHASHSIZE * sizeof(define_t *));
	Parse_AddGlobalDefinesToSource(source);
	return source;
}

/*
===============
Parse_FreeSource
===============
*/
static void Parse_FreeSource(source_t * source)
{
	script_t       *script;
	token_t        *token;
	define_t       *define;
	indent_t       *indent;
	int             i;

	//Parse_PrintDefineHashTable(source->definehash);
	//free all the scripts
	while(source->scriptstack)
	{
		script = source->scriptstack;
		source->scriptstack = source->scriptstack->next;
		Parse_FreeScript(script);
	}
	//free all the tokens
	while(source->tokens)
	{
		token = source->tokens;
		source->tokens = source->tokens->next;
		Parse_FreeToken(token);
	}
	for(i = 0; i < DEFINEHASHSIZE; i++)
	{
		while(source->definehash[i])
		{
			define = source->definehash[i];
			source->definehash[i] = source->definehash[i]->hashnext;
			Parse_FreeDefine(define);
		}
	}
	//free all indents
	while(source->indentstack)
	{
		indent = source->indentstack;
		source->indentstack = source->indentstack->next;
		Z_Free(indent);
	}
	//
	if(source->definehash)
		Z_Free(source->definehash);
	//free the source itself
	Z_Free(source);
}

#define MAX_SOURCEFILES   64

source_t       *sourceFiles[MAX_SOURCEFILES];

/*
===============
Parse_LoadSourceHandle
===============
*/
int Parse_LoadSourceHandle(const char *filename)
{
	source_t       *source;
	int             i;

	for(i = 1; i < MAX_SOURCEFILES; i++)
	{
		if(!sourceFiles[i])
			break;
	}
	if(i >= MAX_SOURCEFILES)
		return 0;
	Parse_SetBaseFolder("");
	source = Parse_LoadSourceFile(filename);
	if(!source)
		return 0;
	sourceFiles[i] = source;
	return i;
}

/*
===============
Parse_FreeSourceHandle
===============
*/
int Parse_FreeSourceHandle(int handle)
{
	if(handle < 1 || handle >= MAX_SOURCEFILES)
		return qfalse;
	if(!sourceFiles[handle])
		return qfalse;

	Parse_FreeSource(sourceFiles[handle]);
	sourceFiles[handle] = NULL;
	return qtrue;
}

/*
===============
Parse_ReadTokenHandle
===============
*/
int Parse_ReadTokenHandle(int handle, pc_token_t * pc_token)
{
	token_t         token;
	int             ret;

	if(handle < 1 || handle >= MAX_SOURCEFILES)
		return 0;
	if(!sourceFiles[handle])
		return 0;

	ret = Parse_ReadToken(sourceFiles[handle], &token);
	strcpy(pc_token->string, token.string);
	pc_token->type = token.type;
	pc_token->subtype = token.subtype;
	pc_token->intvalue = token.intvalue;
	pc_token->floatvalue = token.floatvalue;
	if(pc_token->type == TT_STRING)
		Parse_StripDoubleQuotes(pc_token->string);
	return ret;
}

/*
===============
Parse_SourceFileAndLine
===============
*/
int Parse_SourceFileAndLine(int handle, char *filename, int *line)
{
	if(handle < 1 || handle >= MAX_SOURCEFILES)
		return qfalse;
	if(!sourceFiles[handle])
		return qfalse;

	strcpy(filename, sourceFiles[handle]->filename);
	if(sourceFiles[handle]->scriptstack)
		*line = sourceFiles[handle]->scriptstack->line;
	else
		*line = 0;
	return qtrue;
}
